﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using Pavilion.DesignerTool.ViewTools.ConnectableListBox;

namespace Pavilion.DesignerTool
{
    public class ContainerNodeWrapper : NodeBaseWrapper, IConnectableItemContainer
    {
        public event Action<NodeBaseWrapper> NodeWrapperRemoved;

        public ContainerNode ContainerNode { get; set; }
        public NotifiableCollection<IConnectableItem> ConnectableItems { get; private set; }
        public NotifiableCollection<IConnectonator> Connectonators { get; private set; }

        public ContainerNodeWrapper(ContainerNode containerNode, NodeScriptWrapper nodeScriptWrapper) : base(containerNode, nodeScriptWrapper)
        {
            this.ContainerNode = containerNode;

            ConnectableItems = new NotifiableCollection<IConnectableItem>();
            Connectonators = new NotifiableCollection<IConnectonator>();

            foreach (NodeBase node in this.ContainerNode.NodeCollection)
                RegisterNodeWrapper(CreateNodeBaseWrapper(node));

            foreach (var link in containerNode.LinkCollection)
                Connectonators.Add(CreateLinkWrapper(link));
        }

        public NodeBaseWrapper AddNode(NodeBase node)
        {
            NodeBaseWrapper nodeWrapper = CreateNodeBaseWrapper(node);
            AddNewNodeWrapper(nodeWrapper);

            return nodeWrapper;
        }

        public void AddNewNodeWrapper(NodeBaseWrapper nodeWrapper)
        {
            ContainerNode.AddNode(nodeWrapper.Node);
            RegisterNodeWrapper(nodeWrapper);

            NodeTransfer.Add(NodeScriptWrapper.ResourceData.ResourceName, NodeScriptWrapper.Name, nodeWrapper.Node);
        }

        public void RemoveNode(NodeBaseWrapper nodeWrapper)
        {
            if (nodeWrapper.Node is EntryNode)
                return;

            nodeWrapper.Dispose();
            ContainerNode.RemoveNode(nodeWrapper.Node.Name);
            ConnectableItems.Remove(nodeWrapper);

            NodeTransfer.Remove(NodeScriptWrapper.ResourceData.ResourceName, NodeScriptWrapper.Name, ContainerNode, nodeWrapper.Node);

            OnNodeWrapperRemoved(nodeWrapper);
        }

        public void AddLink(NodeBaseWrapper startNode, NodeBaseWrapper endNode)
        {
            AddLink(startNode, endNode, null, OpenCriteria.Single);
        }

        public void AddLink(NodeBaseWrapper startNode, NodeBaseWrapper endNode, IEnumerable<ILock> lockCollection, OpenCriteria openCriteria)
        {
            if (ContainsLink(startNode, endNode))
                return;

            Link link = this.ContainerNode.LinkNode(startNode.Node, endNode.Node, lockCollection, openCriteria);

            if (link != null)
            {
                LinkWrapper linkWrapper = CreateLinkWrapper(link, startNode, endNode);
                Connectonators.Add(linkWrapper);
                LinkTransfer.Add(linkWrapper);
            }
        }

        public bool ContainsLink(NodeBaseWrapper startNode, NodeBaseWrapper endNode)
        {
            return GetLinkWrapper(startNode, endNode) != null;
        }

        public LinkWrapper GetLinkWrapper(NodeBaseWrapper startNode, NodeBaseWrapper endNode)
        {
            return Connectonators.Select(x => x as LinkWrapper).FirstOrDefault(x => x.Start == startNode && x.End == endNode);
        }

        internal IConnectableItem GetNode(string name)
        {
            try { return ConnectableItems.First((connectableItem) => connectableItem.Name == name); }
            catch { return null; }
        }

        public override string ToString()
        {
            return base.ToString() + " (" + Name + ")";
        }

        private void RegisterNodeWrapper(NodeBaseWrapper nodeWrapper)
        {
            nodeWrapper.ParentContainerNodeWrapper = this;
            nodeWrapper.NodeScriptWrapper = base.NodeScriptWrapper;
            ConnectableItems.Add(nodeWrapper);
        }

        private void RemoveLink(LinkWrapper linkWrapper)
        {
            linkWrapper.Disposed -= new Action<LinkWrapper>(LinkWrapper_Disposed);
            Connectonators.Remove(linkWrapper);
        }

        private LinkWrapper CreateLinkWrapper(Link link)
        {
            return CreateLinkWrapper(link, ConnectableItems.First(x => (x as NodeBaseWrapper).Node == link.StartingNode), ConnectableItems.First(x => (x as NodeBaseWrapper).Node == link.EndingNode));
        }

        private LinkWrapper CreateLinkWrapper(Link link, IConnectableItem startingNode, IConnectableItem endingNode)
        {
            LinkWrapper linkWrapper = new LinkWrapper(link, startingNode, endingNode, this);
            linkWrapper.Disposed += new Action<LinkWrapper>(LinkWrapper_Disposed);

            return linkWrapper;
        }

        protected override void SetNodeScriptWrapper(NodeScriptWrapper value)
        {
            base.SetNodeScriptWrapper(value);

            if (ConnectableItems == null)
                return;

            foreach (NodeBaseWrapper nodeWrapper in ConnectableItems)
                nodeWrapper.NodeScriptWrapper = value;
        }

        private void OnNodeWrapperRemoved(NodeBaseWrapper nodeWrapper)
        {
            if (NodeWrapperRemoved != null)
                NodeWrapperRemoved(nodeWrapper);
        }

        private void LinkWrapper_Disposed(LinkWrapper linkWrapper)
        {
            RemoveLink(linkWrapper);
        }
    }
}
